/*
 * Copyright 2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.pajamas.starter.integration.spring.caching;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import com.github.benmanes.caffeine.cache.Weigher;

import org.apache.commons.lang3.ObjectUtils;
import org.pajmas.daydream.building.AbstractEzBuilder;

import java.util.concurrent.TimeUnit;

/**
 * @author william
 * @since 2024/1/22
 */
public class CaffeineBuilder extends AbstractEzBuilder<Caffeine<Object, Object>> {
    private CaffeineBuilder() {
    }

    public static CaffeineBuilder newBuilder() {
        return new CaffeineBuilder();
    }

    private Integer initialCapacity;
    private Integer maximumSize;
    private Long maximumWeight;
    private Weigher<Object, Object> weigher;
    private Expiry<Object, Object> expiry;
    private Long expireAfterWriteNanos;
    private Long expireAfterAccessNanos;
    private Long refreshAfterWriteNanos;

    public CaffeineBuilder initialCapacity(int initialCapacity) {
        this.initialCapacity = initialCapacity;
        return this;
    }

    public CaffeineBuilder size(int threshold) {
        this.maximumSize = threshold;
        return this;
    }

    /**
     * @param threshold threshold
     * @param weigher   weigher
     * @return org.pajamas.starter.integration.spring.caching.PajamasCaffeineCacheBuilder
     * @author william
     * @see Weigher
     * @since 2024/1/23
     */
    public CaffeineBuilder weight(long threshold, Weigher<Object, Object> weigher) {
        this.maximumWeight = threshold;
        this.weigher = weigher;
        return this;
    }

    /**
     * @param expiry expiry
     * @return org.pajamas.starter.integration.spring.caching.PajamasCaffeineCacheBuilder
     * @author william
     * @see Expiry
     * @since 2024/1/24
     */
    public CaffeineBuilder expiry(Expiry<Object, Object> expiry) {
        this.expiry = expiry;
        return this;
    }

    public CaffeineBuilder expireAfterWrite(long threshold, TimeUnit unit) {
        this.expireAfterWriteNanos = unit.toNanos(threshold);
        return this;
    }

    public CaffeineBuilder expireAfterAccess(long threshold, TimeUnit unit) {
        this.expireAfterAccessNanos = unit.toNanos(threshold);
        return this;
    }

    public CaffeineBuilder refreshAfterWrite(long threshold, TimeUnit unit) {
        this.refreshAfterWriteNanos = unit.toNanos(threshold);
        return this;
    }

    @Override
    public Caffeine<Object, Object> internalBuild() {
        Caffeine<Object, Object> caffeine = Caffeine
                .newBuilder()
                .initialCapacity(ObjectUtils.firstNonNull(this.initialCapacity, 16));

        if (this.weigher != null && this.maximumWeight != null) {
            caffeine.weigher(this.weigher).maximumWeight(this.maximumWeight);
        }
        if (this.maximumSize != null) {
            caffeine.maximumSize(this.maximumSize);
        }

        if (this.expiry != null) {
            caffeine.expireAfter(this.expiry);
        }
        if (this.expireAfterAccessNanos != null) {
            caffeine.expireAfterAccess(this.expireAfterAccessNanos, TimeUnit.NANOSECONDS);
        }
        if (this.expireAfterWriteNanos != null) {
            caffeine.expireAfterWrite(this.expireAfterWriteNanos, TimeUnit.NANOSECONDS);
        }

        if (this.refreshAfterWriteNanos != null) {
            caffeine.refreshAfterWrite(this.refreshAfterWriteNanos, TimeUnit.NANOSECONDS);
        }

        return caffeine;
    }

    public interface Customizer extends AbstractEzBuilder.Customizer<Caffeine<Object, Object>, CaffeineBuilder> {
    }
}
